﻿using Quartz;
using Quartz.Impl;
using Quartz.Impl.AdoJobStore.Common;
using Quartz.Util;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using XJay.Scheduler.Common.CustomException;
using XJay.Scheduler.JobEntity;
using Quartz.Simpl;
using Quartz.Impl.AdoJobStore;
using XJay.Scheduler.Repositories;
using XJay.Scheduler.Common.Helper;

namespace XJay.Scheduler
{
    /// <summary>
    /// 调度器管理
    /// </summary>
    public class SchedulerrManger : ISchedulerrManger
    {

        /// <summary>
        /// 数据连接
        /// </summary>
        private IDbProvider dbProvider;
        /// <summary>
        /// 调度器
        /// </summary>
        private IScheduler _scheduler;
        public SchedulerrManger()
        {
            dbProvider = new DbProvider(AppConfig.DbProviderName, AppConfig.ConnectionString);
            
        }
        /// <summary>
        /// 开启调度器
        /// </summary>
        /// <returns></returns>
        public async Task<bool> StartSchedulerAsync()
        {
            //初始化数据库表结构
            await InitDBTableAsync();
            /// <summary>
            /// 初始化调度器
            /// </summary>
            await InitSchedulerrAsync();
            //开启调度器
            if (_scheduler.InStandbyMode)
            {
                await _scheduler.Start();
            }
            return _scheduler.InStandbyMode;
        }
        /// <summary>
        /// 初始化Schedulerr
        /// </summary>
        private async Task InitSchedulerrAsync()
        {
            if (_scheduler == null)
            {
                DBConnectionManager.Instance.AddConnectionProvider("default", dbProvider);
                var serializer = new JsonObjectSerializer();
                serializer.Initialize();
                var jobStore = new JobStoreTX
                {
                    DataSource = "default",
                    TablePrefix = "QRTZ_",
                    InstanceId = "AUTO",
                    DriverDelegateType = typeof(SqlServerDelegate).AssemblyQualifiedName,
                    ObjectSerializer = serializer,
                };
                DirectSchedulerFactory.Instance.CreateScheduler("bennySchedulerr", "AUTO", new DefaultThreadPool(), jobStore);
                _scheduler = await SchedulerRepository.Instance.Lookup("bennySchedulerr");
            }
        }
        /// <summary>
        /// 初始化数据库表
        /// </summary>
        /// <returns></returns>
        private async Task InitDBTableAsync()
        {
            IRepositorie repositorie = RepositorieFactory.CreateRepositorie(dbProvider);
            await repositorie.InitTable();
        }


        public async Task AddCronJobAsync(CronEntity param!!)
        {
            //检查任务是否已存在
            var jobKey = new JobKey(param.JobName, param.JobGroup);
            if (await _scheduler.CheckExists(jobKey))
            {
                throw new XJayException("任务已存在");
            }
            if (CronExpression.IsValidExpression(param.Cron) == false)
            {
                throw new XJayException("Cron表达式格式错误");
            }
            var dllPath = AppDomain.CurrentDomain.BaseDirectory + param.DllFileName;
            if(!File.Exists(dllPath))
            {
                throw new XJayException("Dll不存在");
            }
            Assembly asm = Assembly.LoadFile(dllPath);
            Type jobType = asm.GetExportedTypes()
                .Where(t => typeof(IJob).IsAssignableFrom(t)) 
                .Where(t => !t.IsAbstract && t.IsClass) 
                .FirstOrDefault(p => p.Name == param.JobClass);

            if (jobType == null) throw new XJayException("该方法不存在");

            IJobDetail job = JobBuilder.Create(jobType).
                 WithDescription(param.Description)
                .WithIdentity(param.JobName, param.JobGroup)
                .Build();

            ITrigger trigger = CreateTrigger(new TriggerEntity() { 
                Cron = param.Cron,
                JobName = param.JobName,
                JobGroup = param.JobGroup
            });

            //告诉Quartz使用我们的触发器来安排作业
            await _scheduler.ScheduleJob(job, trigger);
        }

        public async Task<bool> DeleteJobAsync(JobKey jobkey!!)
        {
            //检查任务是否已存在
            if (!await _scheduler.CheckExists(jobkey))
            {
                throw new XJayException("任务不存在");
            }

            return await _scheduler.DeleteJob(jobkey);
        }

        /// <summary>
        ///  为已有的job新增触发器
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        /// <exception cref="XJayException"></exception>
        public async Task AddTriggerJobAsync(TriggerEntity param!!)
        {
            var jobKey = new JobKey(param.JobName, param.JobGroup);
            //检查任务是否已存在
            if (!await _scheduler.CheckExists(jobKey))
            {
                throw new XJayException("任务不存在");
            }

            ITrigger trigger = CreateTrigger(param);

            await _scheduler.ScheduleJob(trigger);
        }

        /// <summary>
        /// 创建触发器
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        public ITrigger CreateTrigger(TriggerEntity param!!)
        {
            return TriggerBuilder.Create()
                   .WithIdentity(param.JobName + Guid.NewGuid().ToString(), param.JobGroup)
                   .WithCronSchedule(param.Cron)//指定cron表达式
                   .ForJob(param.JobName,param.JobGroup)
                   .Build();
        }
    }
}
